﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

using System.Xml;
using System.Xml.Serialization;

namespace NWT.Testing.Library.Entities.ProcessValidation
{
    public enum ShapeTypes
    {
        RootShape,
        VariableDeclarationShape,
        DNFPredicateShape,
        CorrelationDeclarationShape,
        StatementRefShape,

        ConstructShape,
        TransformShape,
        MessageDeclarationShape,
        MessageAssignmentShape,
        VariableAssignmentShape,
        CallRulesShape,
        RulesParameterRefShape,

        DecisionShape,
        DecisionBranchShape,
        ListenShape,
        ListenBranchShape,
        ParallelShape,
        ParallelBranchShape,
        WhileShape,
        TaskShape, // Group
        ScopeShape,
        CatchShape,
        ThrowShape,

        ReceiveShape,
        SendShape,
        CallShape,
        ParameterShape,
        ExecShape,

        DelayShape,
        TerminateShape,
        SuspendShape,

        MessageRefShape,
        MessagePartRefShape
    }

    public enum FlatteningPrefixType
    {
        None,
        Indentation,
        ParentName
    }

    /// <summary>
    /// A ProcessFlow is the definition of the structure of an orchestration, 
    /// composed of the hierarchy of steps declared in the orchestration designer
    /// </summary>
    [Serializable]
    public class ProcessFlow : ShapeInfo
    {
        protected string _processName;

        [XmlIgnore()]
        public string ProcessName
        {
            get { return this._processName; }
            set { this._processName = value; }
        }

        public ProcessFlow():base()
        {
        }

        /// <summary>
        /// Generates a ProcessFlow from the trace symbols found in the Mgmt Db
        /// </summary>
        /// <param name="processSymbols">Trace symbols</param>
        /// <returns>An instance of ProcessFlow</returns>
        public static ProcessFlow DeserializeProcessFlow(string processSymbols)
        {
            XmlSerializer ser = new XmlSerializer(typeof(ProcessFlow));

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(processSymbols);
            XmlNode processFlowNode = doc.SelectSingleNode("/XsymFile/ProcessFlow");

            return (ProcessFlow)ser.Deserialize(new StringReader(processFlowNode.OuterXml));
        }

        /// <summary>
        /// Used to convert the ProcessFlow (ie: orchestration definition) to a DebugTrace
        /// (ie: trace generated by BizTalk when executing an instance of the orchestration)
        /// </summary>
        /// <returns></returns>
        public DebugTrace Flatten(FlatteningPrefixType prefixType)
        {
            DebugTrace result = new DebugTrace();

            foreach (ShapeInfo shape in this.children.FindAll(si => si.shapeType != ShapeTypes.DNFPredicateShape
                                                            && si.shapeType != ShapeTypes.MessageAssignmentShape
                                                            && si.shapeType != ShapeTypes.TransformShape
                                                            && si.shapeType != ShapeTypes.MessageDeclarationShape
                                                            && si.shapeType != ShapeTypes.MessageRefShape
                                                            && si.shapeType != ShapeTypes.MessagePartRefShape
                                                            && si.shapeType != ShapeTypes.VariableDeclarationShape
                                                            && si.shapeType != ShapeTypes.RulesParameterRefShape
                                                            && si.shapeType != ShapeTypes.CorrelationDeclarationShape
                                                            && si.shapeType != ShapeTypes.StatementRefShape
                                                            && si.shapeType != ShapeTypes.ParameterShape))
            {
                DebugShape ds = new DebugShape();
                ds.RepeatCount = 1;
                ds.ShapeID = shape.ShapeID;
                ds.shapeText = shape.shapeText;
                ds.shapeType = shape.shapeType;
                ds.Completed = true;

                string prefix = String.Empty;
                switch(prefixType)
                {
                    case FlatteningPrefixType.Indentation:
                        prefix = " - ";
                        break;
                    case FlatteningPrefixType.ParentName:
                        prefix = String.Format("[{0}] ", shape.shapeText);
                        break;
                    default:
                        break;
                }
                
                result.TraceDetails.Add(ds);
                result.TraceDetails.AddRange(this.flattenShape(shape, prefixType, prefix));
            }

            return result;
        }

        private List<DebugShape> flattenShape(ShapeInfo shape, FlatteningPrefixType prefixType, string currentPrefix)
        {
            List<DebugShape> result = new List<DebugShape>();
            

            foreach (ShapeInfo child in shape.children.FindAll(si => si.shapeType != ShapeTypes.DNFPredicateShape
                                                            && si.shapeType != ShapeTypes.MessageAssignmentShape
                                                            && si.shapeType != ShapeTypes.TransformShape
                                                            && si.shapeType != ShapeTypes.MessageDeclarationShape
                                                            && si.shapeType != ShapeTypes.MessageRefShape
                                                            && si.shapeType != ShapeTypes.MessagePartRefShape
                                                            && si.shapeType != ShapeTypes.VariableDeclarationShape
                                                            && si.shapeType != ShapeTypes.RulesParameterRefShape
                                                            && si.shapeType != ShapeTypes.CorrelationDeclarationShape
                                                            && si.shapeType != ShapeTypes.StatementRefShape
                                                            && si.shapeType != ShapeTypes.ParameterShape))
            {
                DebugShape ds = new DebugShape();
                ds.RepeatCount = 1;
                ds.ShapeID = child.ShapeID;
                ds.shapeText = currentPrefix + child.shapeText;
                ds.shapeType = child.shapeType;
                ds.Completed = true;

                string prefix = String.Empty;
                switch (prefixType)
                {
                    case FlatteningPrefixType.Indentation:
                        prefix = currentPrefix + " - ";
                        break;
                    case FlatteningPrefixType.ParentName:
                        prefix = currentPrefix + String.Format("[{0}] ", child.shapeText);
                        break;
                    default:
                        break;
                }

                result.Add(ds);
                result.AddRange(this.flattenShape(child, prefixType, prefix));
            }

            return result;
        }
    }



    [Serializable]
    public class ShapeInfo
    {
        protected ShapeTypes _shapeType;
        protected Guid _shapeID;
        protected string _shapeText;
        protected List<ShapeInfo> _children;

        public ShapeTypes shapeType
        {
            get { return this._shapeType; }
            set { this._shapeType = value; }
        }
        public Guid ShapeID
        {
            get { return this._shapeID; }
            set { this._shapeID = value; }
        }
        public string shapeText
        {
            get { return this._shapeText; }
            set { this._shapeText = value; }
        }

        public List<ShapeInfo> children
        {
            get
            {
                if (this._children == null)
                    this._children = new List<ShapeInfo>();
                return this._children;
            }
        }


        public ShapeInfo()
        {
        }

        public ShapeInfo GetShape(Guid shapeID, ref string shapePath)
        {
            ShapeInfo searchedShape = null;
            
            string tempShapePath = String.Format("{0}/{1}", shapePath, this.shapeText);

            if (this.ShapeID == shapeID)
            {
                shapePath = tempShapePath;
                searchedShape = this;
            }
            else
            {
                foreach (ShapeInfo s in this.children)
                {
                    searchedShape = s.GetShape(shapeID, ref tempShapePath);
                    if (searchedShape != null)
                    {
                        shapePath = tempShapePath;
                        break;
                    }
                }
            }

            return searchedShape;
        }
    }
}
